package org.jb.coinche.server.user.room.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.jb.coinche.client.gin.RoomName;
import org.jb.coinche.server.rooms.RoomScoped;
import org.jb.coinche.server.user.room.RoomUsers;
import org.jb.coinche.server.user.room.RoomUsersListener;
import org.jb.coinche.server.user.room.RoomUsersNotifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;

/**
 * 
 * @author jeremie
 *
 */
@RoomScoped
public class RoomUsersImpl implements RoomUsers, RoomUsersNotifier {

	private static final Logger logger = LoggerFactory.getLogger(RoomUsersImpl.class);
	
	/**
	 * 
	 * @author jeremie
	 *
	 */
	private static class User {
		
		/**
		 * 
		 * @param password
		 */
		public User() {}
		
		/**
		 * 
		 * @return
		 */
		public Date getLastActivity() {
			return lastActivity;
		}
		
		/**
		 * 
		 */
		public void notifyActivity() {
			this.lastActivity = new Date();
		}
		
		private Date lastActivity = new Date();
	}
	
	/*
	 * 
	 */
	private void checkUsers() {
		logger.debug( "Checking user activity in room {}...", room );
		Collection<String> disconnectedUsers = null;
		
		Date date = new Date( new Date().getTime() - 5 * 60 * 1000 );
		
		synchronized( users ) { 
			for( Map.Entry<String, User> entry : users.entrySet() ) {
				if (entry.getValue().getLastActivity().before( date )) {
					
					if (disconnectedUsers == null ) {
						disconnectedUsers = new ArrayList<String>();
					}
					
					disconnectedUsers.add( entry.getKey() );
				}
			}
			
			if (disconnectedUsers != null) {
				for( String user : disconnectedUsers ) {
					users.remove(user);
				}
				
				if (users.isEmpty() && future != null) {
					future.cancel(false);
					future = null;
					
					logger.debug( "Stopping user activity check of room {}", room );
				}
			}
		}
		
		if (disconnectedUsers != null) {
			for( String user : disconnectedUsers ) {
				notifyUserDisconnected(user);
			}
		}
		
	}
	
	/*
	 * (non-Javadoc)
	 * @see org.jb.coinche.server.user.room.RoomUsers#getUsers()
	 */
	@Override
	public String[] getUsers() {
		synchronized( users ) {
			return users.keySet().toArray( new String[0] );
		}
	}
	
	/*
	 * (non-Javadoc)
	 * @see org.jb.coinche.server.user.room.RoomUsersNotifier#notifyActivity(java.lang.String)
	 */
	@Override
	public void notifyActivity(String user) {
		
		logger.debug( "activity of user {} in room {}", user, room );
		
		synchronized( users ) {
			User u = users.get( user );
			if (u == null) {
				users.put( user, new User() );
				notifyUserConnected( user );
				
				if (future == null) {
					logger.debug( "Starting user activity check of room {}", room );

					future = executor.scheduleWithFixedDelay( new Runnable() {
						@Override
						public void run() {
							checkUsers();
						}			
					}, 5, 5, TimeUnit.MINUTES );
				}
			} else {
				u.notifyActivity();
			}
		}
	}
	
	/*
	 * 
	 */
	private void notifyUserConnected( String user ) {
		logger.info("User {} connected in room {}", user, room );
		
		if (listeners == null)
			return;

		for( RoomUsersListener listener : listeners ) {
			listener.onUserConnected(user);
		}
	}
	
	/*
	 * 
	 */
	private void notifyUserDisconnected( String user ) {
		logger.info("User {} disconnected in room {}", user, room );

		if (listeners == null)
			return;
		
		for( RoomUsersListener listener : listeners ) {
			listener.onUserDisconnected(user);
		}
	}

	private HashMap< String, User > users = new HashMap< String, User >();
	
	@Inject(optional=true)
	private Set<RoomUsersListener> listeners;
	
	@Inject @RoomName
	private String room;
	
	@Inject
	private ScheduledExecutorService executor;
	
	private ScheduledFuture<?> future;
}
